package com.itheima.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;


import com.itheima.constant.*;
import com.itheima.context.BaseContext;
import com.itheima.dto.EmployeeDTO;
import com.itheima.dto.EmployeeLoginDTO;
import com.itheima.dto.EmployeePageQueryDTO;
import com.itheima.dto.PasswordEditDTO;
import com.itheima.exception.*;
import com.itheima.mapper.EmployeeMapper;
import com.itheima.pojo.Employee;
import com.itheima.properties.JwtProperties;
import com.itheima.result.PageBean;
import com.itheima.service.EmployeeService;
import com.itheima.vo.EmployeeLoginVO;
import com.itheima.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class EmployeeServiceImpl implements EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public EmployeeLoginVO EmployeeLogin(EmployeeLoginDTO employeeLoginDTO) {

//        redisTemplate.opsForValue().set("status",1,10,TimeUnit.SECONDS);



        String key = RedisConstant.ACCOUNT_PASSWORD_ERROR_COUNT;
        String lockKey =RedisConstant.ACCOUNT_LOCK+employeeLoginDTO.getUsername();

        // 检查键是否存在
        if (!redisTemplate.hasKey(key)) {
            // 用户首次登录，设置初始失败次数为1，并设置过期时间为一分钟
            redisTemplate.opsForValue().set(key,0, Duration.ofSeconds(60));

        }
        if (redisTemplate.hasKey(lockKey)){
           Long time= redisTemplate.getExpire(lockKey,TimeUnit.SECONDS);
            throw new AccountLockedException(String.format(MessageConstant.ACCOUNT_LOCKED_TIME,time));
        }


        Employee employee = employeeMapper.getByUsername(employeeLoginDTO);
        if (employee == null) {
            throw new AccountLockedException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //md5加密密码
//        String password = DigestUtils.md5DigestAsHex(employeeLoginDTO.getPassword().getBytes(StandardCharsets.UTF_8));
//        String salt = BCrypt.gensalt();
//        String hashpw = BCrypt.hashpw(employeeLoginDTO.getPassword(), salt);
//        使用 Bcrypt 改造一下苍穹的加密方式
        if (!BCrypt.checkpw(employeeLoginDTO.getPassword(),employee.getPassword())) {

            //设置一下一分钟内密码输入错误统计次数用redis
            redisTemplate.opsForValue().increment(key, 1);
            if ((Integer)redisTemplate.opsForValue().get(key) >= 3 && !redisTemplate.hasKey(lockKey)) {
                redisTemplate.opsForValue().set(lockKey, employeeLoginDTO.getUsername(), 60, TimeUnit.SECONDS);
            }

            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }
        if (employee.getStatus() == StatusConstant.DISABLE) {
            throw new AccountDisableException(MessageConstant.ACCOUNT_DISABLE);
        }
        //如果在一分钟内登录成功就把key删除从新计数
        redisTemplate.delete(key);
        Map<String, Object> map = new HashMap<>();
        map.put(JwtClaimsConstant.EMP_ID, employee.getId());
        map.put(JwtClaimsConstant.NAME, employee.getName());
        map.put(JwtClaimsConstant.USERNAME, employee.getUsername());
        String jwt = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), map);
        redisTemplate.opsForValue().set("token",jwt,60,TimeUnit.SECONDS);
        Long empId = employee.getId();
        return new EmployeeLoginVO(empId, employee.getUsername(), employee.getName(), jwt);
    }

    @Override
    public void logout() {
        //退出即清空cookie和token
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                cookie.setMaxAge(0);
                response.addCookie(cookie);
            }
        }
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
    }

    @Override
    @Transactional
    public void updatePassword(PasswordEditDTO passwordEditDTO) {
        Long empId = BaseContext.getCurrentId();
        Employee employee = employeeMapper.getById(empId);
        if (employee == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        if (employee.getStatus() == StatusConstant.DISABLE) {
            throw new AccountDisableException(MessageConstant.ACCOUNT_DISABLE);
        }
        //md5加密
//        String oldPassword = DigestUtils.md5DigestAsHex(passwordEditDTO.getOldPassword().getBytes(StandardCharsets.UTF_8));
        //使用 Bcrypt 改造一下苍穹的加密方式
        if (!BCrypt.checkpw(passwordEditDTO.getOldPassword(),employee.getPassword())) {
            throw new PasswordEditFailedException(MessageConstant.PASSWORD_EDIT_FAILED);
        }
        passwordEditDTO.setEmpId(empId);
        //使用 Bcrypt 改造一下苍穹的加密方式
        String salt = BCrypt.gensalt();
        String hashpw = BCrypt.hashpw(passwordEditDTO.getNewPassword(), salt);
        passwordEditDTO.setNewPassword(hashpw);

        employeeMapper.updatePassword(passwordEditDTO);
    }

    @Override
    public PageBean page(EmployeePageQueryDTO employeePageQueryDTO) {
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
        Page<Employee> employees = employeeMapper.pageSelect(employeePageQueryDTO);
        return new PageBean(employees.getTotal(), employees.getResult());

    }

    @Override
    public void add(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO,employee);
        //使用 Bcrypt 改造一下苍穹的加密方式
        String salt = BCrypt.gensalt();
        String hashpw = BCrypt.hashpw(PasswordConstant.DEFAULT_PASSWORD, salt);
        employee.setPassword(hashpw);

        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.addEmployee(employee);
    }

    @Override
    public void updateStatus(Integer status, Long id) {
        Employee employee = new Employee();
        employee.setStatus(status);
        employee.setId(id);
        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.updateEmployee(employee);
    }

    @Override
    public Employee selectById(Integer id) {
        Employee employee = employeeMapper.findById(id);
        return employee;
    }

    @Override
    public void update(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        BeanUtils.copyProperties(employeeDTO,employee);
        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.updateEmployee(employee);
    }
}
